package se.teamscape.client.rpc;

import java.util.HashMap;
import java.util.Map;

import net.customware.gwt.dispatch.client.DispatchAsync;
import net.customware.gwt.dispatch.shared.Action;
import net.customware.gwt.dispatch.shared.Result;
import net.customware.gwt.presenter.client.EventBus;
import se.teamscape.client.event.RpcDoneEvent;
import se.teamscape.client.event.RpcStartedEvent;

import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.inject.Inject;

/**
 * Dispatcher which support caching of data in memory. Code as shown at
 * http://blog
 * .hivedevelopment.co.uk/2009/08/google-web-toolkit-gwt-mvp-example.html with
 * some minor changes.
 * 
 */
public class CachingDispatchAsync implements DispatchAsync {

    private final DispatchAsync mDispatcher;
    private final Map<Action<Result>, Result> mCache = new HashMap<Action<Result>, Result>();
    private final EventBus mEventBus;

    @Inject
    public CachingDispatchAsync(final DispatchAsync dispatcher, final EventBus eventBus) {
        this.mDispatcher = dispatcher;
        this.mEventBus = eventBus;
    }

    /*
     * (non-Javadoc)
     * 
     * @see net.customware.gwt.dispatch.client.DispatchAsync#execute(A,
     * com.google.gwt.user.client.rpc.AsyncCallback)
     */
    public <A extends Action<R>, R extends Result> void execute(final A action, final AsyncCallback<R> callback) {
        mDispatcher.execute(action, callback);
    }

    /**
     * Execute the give Action. If the Action was executed before it will get
     * fetched from the cache
     * 
     * @param <A>
     *            Action implementation
     * @param <R>
     *            Result implementation
     * @param action
     *            the action
     * @param callback
     *            the callback
     */
    @SuppressWarnings("unchecked")
    public <A extends Action<R>, R extends Result> void executeWithCache(final A action, final AsyncCallback<R> callback) {
        final Result r = mCache.get(action);

        if (r != null) {
            callback.onSuccess((R) r);
        }
        else {
            mEventBus.fireEvent(new RpcStartedEvent());
            mDispatcher.execute(action, new AsyncCallback<R>() {

                public void onFailure(Throwable caught) {
                    mEventBus.fireEvent(new RpcDoneEvent());
                    callback.onFailure(caught);
                }

                public void onSuccess(R result) {
                    mEventBus.fireEvent(new RpcDoneEvent());
                    mCache.put((Action) action, (Result) result);
                    callback.onSuccess(result);
                }

            });
        }
    }

    /**
     * Clear the cache
     */
    public void clear() {
        mCache.clear();
    }
}